TagManager = function( domElem )
{
    ///////////////////////////////////////////////////////////////////////////
    // private functions
    ///////////////////////////////////////////////////////////////////////////
    this.onClick = function( event )
    {
        var tagManager = event.currentTarget.tagManager;
        var curID      = event.currentTarget.id;

        tagManager.m_tags[curID].state = (tagManager.m_tags[curID].state+1) % tagManager.m_stateClasses.length;
        tagManager.updateElemView( curID );

        $(tagManager).trigger( 'onchange', [curID, tagManager.m_tags[curID].state] );
    }

    this.updateElemView = function( tag_id )
    {
        var tagElem = null;
        for ( var k = 0; k < this.m_domElem.childNodes.length; k++ )
        {
            if ( this.m_domElem.childNodes[k].id == tag_id )
            {
                tagElem = this.m_domElem.childNodes[k];
                break;
            }
        }

        if ( tagElem == null ) return;
        
        $(tagElem).attr('class', this.m_stateClasses[this.m_tags[tag_id].state]);
    }
    
    this.m_tags = [];
    this.m_domElem = domElem;
    this.m_stateClasses = [];
    this.m_groupList = [];
    
    domElem.innerHTML = '';
}

//all previous manager's data will be deleted
TagManager.prototype.setTagList = function( tagList, groupList, stateClasses, initialState )
{
    if ( tagList.length == 0 )
        this.m_domElem.innerHTML = 'No tags';
        
    //clone!
    this.m_stateClasses = stateClasses.slice(0);
    
    this.m_groupList = [];
    for ( var g = 0; g < groupList.length; g++ )
    {
        this.m_groupList[groupList[g].id] = groupList[g];
        this.m_groupList[groupList[g].id].listTags = [];
    }
    
    this.m_tags = [];
    for ( var k = 0; k < tagList.length; k++ )
    {
        var curState = true;
        if ( typeof(tagList[k].state) != 'undefined' )
            curState = tagList[k].state;
        else if ( typeof( initialState ) != 'undefined' )
            curState = initialState;
        else 
        {
            alert('Missing initial state for tag' + tagList[k].tag);
            return;
        }
        
        if ( typeof(this.m_stateClasses[curState]) == 'undefined' )
        {
            alert('Missing state class for tag' + tagList[k].tag);
            return;
        }

        this.m_tags[tagList[k].id] = { state: curState, tag: tagList[k].tag, groupid: tagList[k].groupid };
        this.m_groupList[tagList[k].groupid].listTags.push(tagList[k].id);
    }
    
    //widget construction
    var firstLine = true;
    for ( var g = 0; g < this.m_groupList.length; g++ )
    {
        if ( typeof(this.m_groupList[g]) == 'undefined' ) continue;
        
        if ( !firstLine )
        {
            // alert('adding br');
            this.m_domElem.appendChild(document.createElement('br'));
        }
        firstLine = false;
        
        var groupName = document.createTextNode(this.m_groupList[g].visible_name + ": ");
        this.m_domElem.appendChild( groupName );
        
        for ( var k = 0; k < this.m_groupList[g].listTags.length; k++ )
        {
            var curTagID = this.m_groupList[g].listTags[k];
            var curTagElem = document.createElement('b');
            var delim = document.createTextNode(" ");
            var curTag = this.m_tags[curTagID];
            curTagElem.id = curTagID;
            // curTagElem.innerHTML = curTag.tag + '(' + this.m_groupList[curTag.groupid].visible_name + ')';
            curTagElem.innerHTML = curTag.tag;
            curTagElem.onclick = this.onClick;

            curTagElem.tagManager = this;
            this.m_domElem.appendChild( curTagElem );
            this.m_domElem.appendChild( delim );
            this.updateElemView( curTagID );
        }
    }
    
    $(this).trigger( 'onchange', [null, null] );
}

TagManager.prototype.getStateByID = function( tag_id )
{
    return this.m_tags[ tag_id ].state;
}

TagManager.prototype.getIDsWithState = function( state )
{
    var listActive = [];
    for (var k in this.m_tags)
        if ( this.m_tags[k].state == state )
            listActive.push(k);
    return listActive;
}

//todo: can be precomputed
TagManager.prototype.getIDsWithStateAsHash = function( state )
{
    var hashActive = {};
    for (var k in this.m_tags)
        if ( this.m_tags[k].state == state )
            hashActive[k] = 1;
    return hashActive;
}